<?php

require_once("definitions.php");
require_once("symbolobject.php");
require_once("numberobject.php");
require_once("listobject.php");
require_once("stringobject.php");

class Reader {

    var $str = null;
    var $i = 0;
    var $len = 0;
    var $sexpr = null;
    var $pounds = array();

    function Reader($str = null) {

        $this->str = $str;
        if ($str) {
            $this->len = strlen($str);
            $this->sexpr = $this->get_sexpr();
        }
    }

    function add_pound_helper($char, $helper) {
        $this->pounds[$char] = $helper;
    }

    function get_token($str = null) {
        if ($this->i >= $this->len)
            return null;
        while ($this->i < $this->len && isInWS($this->str[$this->i]))
            $this->i = $this->i + 1;
        if ($this->i == $this->len)
            return null;

        if ($this->str[$this->i] == '#') {
            $this->i = $this->i + 2;
            return $this->pounds[$this->str[$this->i - 1]]($this->get_token());
        }
        if (isInSPECIAL($this->str[$this->i])) {
            $this->i = $this->i + 1;
            return $this->str[$this->i - 1];
        } elseif ($this->str[$this->i] == '"') {
            $str = "";
            $this->i = $this->i + 1;
            while (($this->str[$this->i] != '"') && ($this->i < $this->len)) {
                if ($this->str[$this->i] == '\\') {
                    $this->i = $this->i + 1;
                    $spchar = $this->str[$this->i];
                    if ($spchar == "n") {
                        $str = $str . "\n";
                    } elseif ($spchar == "t") {
                        $str = $str . "\t";
                    }
                } else {
                    $str = $str . $this->str[$this->i];
                }
                $this->i = $this->i + 1;
            }
            $this->i = $this->i + 1;
            return new StringObject($str);
        } else {
            $tok = "";
            while ($this->i < $this->len - 1) {
                if (isInSYNTAX($this->str[$this->i]))
                    break;
                else {
                    $tok = $tok . $this->str[$this->i];
                    $this->i = $this->i + 1;
                }
            }
            if (!isInSYNTAX($this->str[$this->i])) {
                $tok = $tok . $this->str[$this->i];
                $this->i = $this->i + 1;
            }
            return Reader::get_simple_token($tok);
        }
    }

    public static function get_simple_token($tok) {
        if (is_numeric($tok))
            return new NumberObject($tok);
        else
            return new SymbolObject($tok);
    }

    function get_sexpr($str = null) {
        global $G_QUOTE, $G_IQUOTE;
        if ($str) {
            $this->i = 0;
            $this->str = $str;
            $this->len = strlen($this->str);
        }

        $expr = null;
        $tok = $this->get_token();
        if ($tok == ')') {
            echo "Error! Unexpected ')'";
            exit();
        } elseif ($tok == "(") {
            $expr = array();
            $tok = $this->get_token();
            while ($tok != ")") {
                if ($tok == '(') {
                    $this->i = $this->i - 1;
                    $expr[] = $this->get_sexpr();
                } elseif ($tok == "'") {
                    $expr[] = new ListObject(array($G_QUOTE, $this->get_sexpr()));
                } elseif ($tok == "`") {
                    $expr[] = new ListObject(array($G_IQUOTE, $this->get_sexpr()));
                } elseif ($tok == null) {
                    echo "Error! Unexpected end of expression";
                    exit();
                } else {
                    $expr[] = $tok;
                }
                $tok = $this->get_token();
            }
            return new ListObject($expr);
        } elseif ($tok == "'") {
            return new ListObject(array($G_QUOTE, $this->get_sexpr()));
        } elseif ($tok == "`") {
            return new ListObject(array($G_IQUOTE, $this->get_sexpr()));
        } else {
            return $tok;
        }
    }

}

